\chapter{第一步}

在本章中，我们将迈出使用Haskell的第一步。我们首先介绍Hugs系统和Prelude标准库，然后解释函数应用的记法，开发我们的第一个Haskell脚本，最后讨论一些关于脚本的语法惯例。

\section{Hugs系统}
正如我们在前一章所看到的，我们可以手工执行一些小的Haskell程序，然而在实践中，我们通常需要一个可以自动执行程序的系统。在这本书中，我们使用一个被称为\textit{Hugs}的交互式系统，它也是使用最广泛的Haskell实现。

Hugs的交互式的本质使得其非常适合教学和制作原型，并且它的性能可以满足绝大多数应用的要求。然而，如果需要更高的性能或独立的可执行程序，也有一些Haskell的编译器是可供选择的，这其中使用最广泛的是Glasgow
Haskell的编译器，这个编译器也有一个和Hugs的运行方式类似的交互式版本，并且该版本可以很容易的在本书中使用。

\section{标准Prelude}

当Hugs系统启动时，它首先加载一个名为\textit{Prelude.hs}的库文件，然后显示一个>提示符，表明系统正在等待用户输入待求值的表达式。例如，这个库文件定义了许多熟知的操作整数的函数，包括加，减，乘，除和求幂五个主要算术运算，如下所示：

\noindent\hspace*{1cm} $>2~+~3$\\
\hspace*{1cm} $5$\\
\hspace*{1cm} $> 2~-~3$\\
\hspace*{1cm} $-1$\\
\hspace*{1cm} $> 2~*~3$\\
\hspace*{1cm} $6$\\
\hspace*{1cm} $> 7~`div`~2$\\
\hspace*{1cm} $3$\\
\hspace*{1cm} $> 2$ \verb|^| $3$\\
\hspace*{1cm} $8$

注意，整数除法操作符记作$`div`$，如果结果是一个真分数，那么将向下圆整到最近的那个整数。

按常规数学惯例，求幂比乘法和除法具有更高的优先级，进而也具有比加法和减法更高的优先级。例如，\verb|2 * 3 ^ 4|表示\verb|2 * (3 ^ 4)|，而\verb|2 + 3 * 4|表示\verb|2 + (3 * 4)|。此外，求幂运算是右结合的，而其他四种算术操作符则是左结合的。例如，\verb|2 ^ 3 ^ 4|意味着\verb|2 ^ (3 ^ 4)|，而\verb|2 - 3 + 4|则指的是\verb|(2 - 3) + 4|。但实际上，在算术表达式里显式使用括号通常比依靠上述惯例表达得更为清楚。 

除了操作整数的函数外，这个库文件还提供了一些有用的列表操作函数。在Haskell中，列表中的元素用方括号括上，并以逗号分隔。一些最常用的列表操作库函数说明如下：

\begin{itemize}
\item 从一个非空列表中选出第一个元素:

\noindent\hspace*{1cm} $> head~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $1$

\item 从一个非空列表中删除第一个元素:

\noindent\hspace*{1cm} $> tail~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $[2,~3,~4,~5]$

\item 选出列表中的第$n$个元素（从0开始计数):

\noindent\hspace*{1cm} $> [1,~2,~3,~4,~5]~!!~2$\\
\hspace*{1cm} $3$

\item 选出列表中的前$n$个元素:

\noindent\hspace*{1cm} $> take~3~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $[1,~2,~3]$

\item 从列表中删除前$n$个元素:

\noindent\hspace*{1cm} $> drop~3~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $[4,~5]$

\item 计算列表的长度:

\noindent\hspace*{1cm} $> length~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $5$

\item 计算数字列表中元素之和:

\noindent\hspace*{1cm} $> sum~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $15$

\item 计算数字列表中元素之积:

\noindent\hspace*{1cm} $> product~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $120$

\item 连接两个列表:

\noindent\hspace*{1cm} $> [1,~2,~3]~++~[4,~5]$\\
\hspace*{1cm} $[1,~2,~3,~4,~5]$

\item 反转列表:

\noindent\hspace*{1cm} $> reverse~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $[5,~4,~3,~2,~1]$

\end{itemize}

针对某些参数值，标准\textit{Prelude}中的一些函数可能产生错误。比如试图除零或从一个空列表中选择第一个元素都会产生一个错误：

\noindent\hspace*{1cm} $> 1~`div`~0$\\
\hspace*{1cm} $Error$\\
\hspace*{1cm} $> head~[~]$\\
\hspace*{1cm} $Error$

实际上，当出现错误时，\textit{Hugs}系统也会产生一个消息，提供一些有关错误原因的信息。

作为参考，附录A介绍了标准Prelude最常用的一些定义，附录B显示了一些特殊的Haskell符号，如\verb|^|和$+$，以及如何使用键盘输入这些符号。

\section{函数应用}

在数学中，将函数应用于参数通常表示为用括号将参数括起来，且将两值相乘往往采用习惯性的表示，将两个值一个接着一个的写即可。例如，在数学中，表达式

\noindent\hspace*{1cm} $f~(a,~b)~+~c~d$

意为将函数$f$应用于两个参数$a$和$b$上，并将结果与$c$和$d$的乘积相加。为了反映函数在语言中的主流位置，在Haskell中函数程序习惯性地使用空格表示，而两值相乘则显式地使用$*$操作符表示。例如，上面的表达式使用Haskell编写如下：

\noindent\hspace*{1cm} $f~a~b + c~*~d$

此外，函数程序拥有比其它操作符更高的优先级。比如，$f~a + b$意为$(f~a) +
b$。下表给出了一些例子，来进一步说明函数记法在数学与Haskell之间的差异。

\begin{table}[htbp]
\label{tab:threesome}
\centering
\begin{tabular}{ll}
\hline
Mathematics & Haskell  \\
\hline
$f(x)$ & f~x \\
$f(x,y)$ & f~x~y \\
$f(g(x))$ & f~(g~x) \\
$f(x,g(y))$ & f~x~(g~y)\\
$f(x)g(y)$ & f~x~*~g~y\\
\hline
\end{tabular}
\end{table}

注意上面的表达式$f~(g~x)$在Haskell中依然需要括号，因为$f~g~
x$本身会被解释为将函数$f$应用于两个参数$g$和$x$。然而其本意却是将$f$应用于一个参数上，该参数即是将函数$g$应用于参数$x$上的结果。同样这个注意项也适用于表达式$f~x~(g~y)$.

\section{Haskell脚本}
除了标准Prelude所提供的函数外，你也可以定义新的函数。你无法在Hugs的>提示符下定义新函数，只能在\textit{脚本}中定义。脚本是一个由一系列定义组成的文本文件。按照惯例，Haskell脚本通常用\textit{.hs}作为文件后缀名以区别于其他种类的文件。

\subsection{我的第一个脚本}
当开发一个Haskell脚本时，保持两个窗口一直打开着是很有用的：一个窗口运行脚本的编辑器，另外一个运行Hugs。举个例子，假设我们启动文本编辑器输入两个函数的定义，并保存脚本到一个名为\textit{test.hs}的文件中：

\noindent\hspace*{1cm} $double~x~= x + x$\\
\hspace*{1cm} $quadruple~x~= double~(double~x)$

相应的，假设我们保持编辑器窗口处于打开状态，而在另外一个窗口中启动Hugs并输入指令使其加载这个新脚本：

\noindent\hspace*{1cm} $> :load test.hs$

现在\textit{Prelude.hs}和\textit{test.hs}都已被加载，两个脚本中的函数都可以自由使用了。比如：

\noindent\hspace*{1cm} $> quadruple~10$\\
\hspace*{1cm} $40$\\
\hspace*{1cm} $> take~(double~2)~[1,~2,~3,~4,~5,~6]$\\
\hspace*{1cm} $[1,~2,~3,~4]$

现在保持Hugs处于启动状态，我们返回到编辑器窗口。将下面两个函数的定义添加到脚本中，并重新保存文件。

\noindent\hspace*{1cm} $factorial~n = product~[~1..n~]$\\
\hspace*{1cm} $average~ns = sum~ns~`div`~length~ns$

我们同样可以这样定义：$average~ns = div~(sum~ns)~(length~ns)$,
但是将\textit{div}放在两个参数中间更加自然。一般情况下，任何接受两个参数的函数都可以写成将函数名用反单引号(`)括上后放在其参数之间的形式。

当脚本被修改后，Hugs不会自动加载它们，所以在使用新定义之前必须执行一个reload命令：

\noindent\hspace*{1cm} $> :reload$\\
\hspace*{1cm} $> factorial~10$\\
\hspace*{1cm} $3628800$\\
\hspace*{1cm} $> average~[1,~2,~3,~4,~5]$\\
\hspace*{1cm} $3$

作为参考，下表总结了一些Hugs中最常用命令的含义。请注意，每条命令都可以通过它的第一个字符进行缩写。例如，$:load$可以缩写为$:l$。命令$:type$将在后面的篇章中详细解释。

\begin{table}[htbp]
\label{tab:threesome}
\centering
\begin{tabular}{ll}
\hline
命令 & 含义\\
\hline
\textit{:load name} & 加载脚本\textit{name} \\
\textit{:reload} & 重新加载当前脚本 \\
\textit{:edit name} & 编辑脚本\textit{name} \\
\textit{:type expr} & 显示\textit{expr}的类型信息 \\
\textit{:?} & 显示所有命令 \\
\textit{:quit} & 退出Hugs \\
\hline
\end{tabular}
\end{table}

\subsection{命名需求}
定义一个新函数时，函数以及其参数的名字必须以小写字母开头，但后面可以跟随零个或多个字母（包括小写和大写），数字，下划线和正向单引号。例如，以下名字都是合法的：

\centerline{$myFun~~~fun1~~~arg~2~~~x’$}

下面列表中的\textit{关键字}在语言中都有着特殊的含义，并且不能作为函数或其参数的名字使用：

\begin{center}
\textbf{case~~class~~data~~default~~deriving~~do~~else}\\
\textbf{if~~import~~in~~infix~~infixl~~infixr~~instance}\\
\textbf{let~~module~~newtype~~of~~then~~type~~where}
\end{center}

按照惯例，在Haskell中列表参数的名字中通常有一个后缀\textit{s}，表明它们可能含有多个值。例如，一个数字列表可能被命名为\textit{ns}，一个包含任意值的列表可能会被命名为\textit{xs}，一个字符列表可能被命名的\textit{css}。

\subsection{布局规则}

在一个脚本中，每个定义必须精确的从相同的列开始。这种\textit{布局规则}使我们能够根据代码缩进确定定义分组。例如脚本：

\noindent\hspace*{1cm} $a = b + c$\\
\hspace*{2cm} $        where $\\
\hspace*{3cm} $            b = 1$\\
\hspace*{3cm} $            c = 2$\\
\hspace*{1cm} $d = a * 2$

通过缩进可以很清楚的看出$b$和$c$是在$a$定义体中使用的局部定义。如果需要，这个分组可以显式的通过花括号将一系列定义括起来，并且定义之间可以用分号隔开。例如，上面的脚本也可以写成：

\noindent\hspace*{1cm} $a = b + c$\\
\hspace*{2cm} $        where $\\
\hspace*{3cm} $            \{b = 1;$\\
\hspace*{3cm} $            c = 2\}$\\
\hspace*{1cm} $d = a * 2$

但一般来说，依赖布局规则来确定定义分组比使用显式语法更加清晰。

\subsection{注释}

除了新的定义，脚本也会包含注释，但注释将被Hugs忽略。Haskell提供了两种类型的注释，分别称为\textit{普通注释}和\textit{嵌套注释}。普通注释以符号-开始，作用延伸到当前行的结尾，如下面的例子所示：

\noindent\hspace*{1cm} — Factorial of a positive integer:\\
\hspace*{1cm} $factorial~n~=~product~[1 . . n ]$\\
\hspace*{1cm} — Average of a list of integers:\\
\hspace*{1cm} $average~ns~= sum~ns~`div`~length~ns$

嵌套注释的开始和结束符号为\{-和-\}，嵌套注释可以跨多行，还可能包含其他注释。嵌套注释在临时删除脚本中的某段定义时特别有用，如下面的例子：

\noindent\hspace*{1cm} \{-\\
\hspace*{1cm} $double~x$\\
\hspace*{1cm} $quadruple~x$\\
\hspace*{1cm} -\}

\section{本章备注}
Hugs系统可从Haskell主页www.haskell.org上自由下载，另外Haskell主页上还提供了其他有用的资源。

\section{习题}

\begin{enumerate}
\item 用括号显式标出下面算术表达式的结合情况：

\noindent\hspace*{1cm} $2$~\verb|^|~$3~*~4$\\
\hspace*{1cm} $2~*~3~+~4~*~5$\\
\hspace*{1cm} $2~+~3~*~4$~\verb|^|~$5$

\item 使用Hugs执行一遍本章所提供的例子

\item
下面的脚本中包含三处语法错误，纠正这些错误并使用Hugs确认你的脚本可以正常工作。

\noindent\hspace*{1cm} $N~ = a~`div`~length~xs$\\
\hspace*{2cm} $       where$\\
\hspace*{3cm} $          a~=~10$\\
\hspace*{2.5cm} $        xs = [1,~2,~3,~4,~5]$

\item Show how the library function last that selects the last element of a
non-empty list could be defined in terms of the library functions introduced
in this chapter. Can you think of another possible definition?

\item Show how the library function \textit{init} that removes the last element from a
non-empty list could similarly be defined in two different ways.
\end{enumerate}
